#include "sdhci.h"
#include "mmc_core.h"

static inline int mmc_host_count(void *data)
{
    int mmc_num = get_mmc_max_num();
    struct mmc_suspend_resume *mmc_sus_res = NULL;

    if (data != NULL) {
        mmc_sus_res = (struct mmc_suspend_resume*) data;
        if (mmc_num > mmc_sus_res->mmc_max_num) {
            mmc_num = mmc_sus_res->mmc_max_num;
        }
    }
    return mmc_num;
}

int mmc_host_suspend(void *data)
{
    struct mmc_suspend_resume *mmc_sus_res = (struct mmc_suspend_resume*) data;
    struct mmc_host *mmc = NULL;
    struct mmc_card *card = NULL;
    struct sdhc_host *host = NULL;
    int mmc_num = mmc_host_count(data);
    int mmc_idx = 0;
    int mmc_need_detect = 0;

    mmc_trace(MMC_TRACE_DEBUG, "mmc host is suspending\n");

    for(; mmc_idx < mmc_num; mmc_idx++) {
        mmc = get_mmc_host(mmc_idx);
        if (mmc == NULL) {
            continue;
        }
        host = mmc->priv;

        (void)mmc_mutex_lock(host->thread_mutex, MMC_MUTEX_WAIT_FOREVER);
        card = mmc->card_cur;
        mmc_need_detect = 0;
        set_mmc_dis_detect(mmc, true);
        if (mmc_sus_res != NULL) {
            if (MMC_DO_DETECT == (mmc_sus_res->flags[mmc_idx])) {
                mmc_need_detect = 1;
            }
        } else {
            if (card != NULL && (!is_card_sdio(card))) {
                mmc_need_detect = 1;
            }
        }

        mmc_trace(MMC_TRACE_DEBUG, "host%d card=%p detect=%d.\n", host->id, card, mmc_need_detect);

        if (card != NULL && mmc_need_detect) {
            sdhci_hndl_plug_event(mmc, false);
        }
        (void)mmc_mutex_unlock(host->thread_mutex);
    }

    mmc_trace(MMC_TRACE_DEBUG, "mmc host is suspended\n");
    return 0;
}

int mmc_host_resume(void *data)
{
    struct mmc_suspend_resume *mmc_sus_res = (struct mmc_suspend_resume*)data;
    struct mmc_host *mmc = NULL;
    struct mmc_card *card = NULL;
    struct sdhc_host *host = NULL;
    int mmc_idx, rv;
    int mmc_need_detect = 0;
    int mmc_num = mmc_host_count(data);
    UINT32 task_id;

    mmc_trace(MMC_TRACE_DEBUG, "mmc host is resuming\n");

    for (mmc_idx = 0; mmc_idx < mmc_num; mmc_idx++) {
        mmc = get_mmc_host(mmc_idx);
        if (mmc == NULL) {
            continue;
        }
        host = (struct sdhc_host *)mmc->priv;
        card = mmc->card_cur;

        // here enable irq vector
        hal_interrupt_unmask((int)host->irq_num);

        mmc_need_detect = 0;
        if (mmc_sus_res != NULL) {
            if (MMC_UNDO_DETECT == (mmc_sus_res->flags[mmc_idx])) {
                mmc_need_detect = 0;
            }
        } else {
            if (card != NULL && is_card_sdio(card)) {
                mmc_need_detect = 0;
            }
        }

        if (card == NULL) {
            mmc_need_detect = 1;
        }

        mmc_trace(MMC_TRACE_DEBUG, "host%d card=%p detect=%d.\n", host->id, card, mmc_need_detect);

        set_mmc_dis_detect(mmc, false);

        if (mmc_need_detect) {
            if (!host->forceSWDetect) {
                (void)mmc_mutex_lock(host->thread_mutex, MMC_MUTEX_WAIT_FOREVER);
                rv = mmc_thread_create(5,
                                   (TSK_ENTRY_FUNC)sd_mci_pre_detect_card,
                                   SDMCI_STACKSIZE,
                                   (AARCHPTR)host,
                                   "sdmci_Pre_detect",
                                   &task_id);
               if (rv) {
                   mmc_trace(MMC_TRACE_ERR, "create thread fail\n");
               }
               mmc_trace(MMC_TRACE_DEBUG, "create thread\n");
               (void)mmc_mutex_unlock(host->thread_mutex);
               return rv;
            }
            //do nothing when forceSWDetect is set.
        } else {
            (void)mmc_mutex_lock(host->thread_mutex, MMC_MUTEX_WAIT_FOREVER);
            (void)mmc_sem_pend(host->sem_id, MMC_SEM_WAIT_FOREVER);

            sdhci_init(host, 0);
            //lint -e613
            mmc_set_clock(mmc, card->iocfg.clock);
            if (!host->forceSWDetect) {
                sdhci_enable_card_detection(host);
            }
            mmc_set_power_mode(mmc, card->iocfg.power_mode);
            mmc_set_timing(mmc, card->iocfg.timing);
            mmc_set_bus_width(mmc, card->iocfg.bus_width);
            switch (card->iocfg.vdd) {
                case VDD_3V3:
                    (void)mmc_voltage_switch(mmc, SIGNAL_VOLT_3V3);
                    break;
                case VDD_1V8:
                    (void)mmc_voltage_switch(mmc, SIGNAL_VOLT_1V8);
                    break;
                default:
                    mmc_printf(0, "err vdd\n");
                    break;
            }
            //lint +e613
            (void)mmc_sem_post(host->sem_id);
            (void)mmc_mutex_unlock(host->thread_mutex);
        }
    }
    mmc_trace(MMC_TRACE_DEBUG, "mmc host is resumed\n");
    return 0;
}
